/**
  ******************************************************************************
  * @file    bsp_can.c
  * @author  Iron
  * @date    2018-07-17
  * @version v1.0
  * @brief   CAN comunicaiton c file
  */

/** @addtogroup GROUP_BSP
  * @{
  */

/* includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "queue.h"
#include "bsp_can.h"

// DEBUG
#include "debug_log.h"
#define LOG_LOCAL_LEVEL DBG_LOG_DEBUG
DBG_LOG_TAG("CAN");

/* private typedef -----------------------------------------------------------*/
/* private define ------------------------------------------------------------*/
/* private macro -------------------------------------------------------------*/
#define BSP_CAN_ENTER_ISR() \
    UBaseType_t uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR()

#define BSP_CAN_EXIT_ISR() \
    portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus)

#define BSP_CAN_IRQ_PRE_PRI  9      /* 5 < PRI < 15*/
#define BSP_CAN_IRQ_SUB_PRI  0

/* private variables ---------------------------------------------------------*/
CAN_HandleTypeDef hcan1;

/* private function prototypes -----------------------------------------------*/
/* private functions ---------------------------------------------------------*/
extern void _Error_Handler(char *file, int line);
void hd_sensor_task_can_rx_isr_cb(BaseType_t *pxHigherPriorityTaskWoken);

/* CAN1 init function 500Kbps */
void MX_CAN1_Init(void)
{
    CAN_FilterTypeDef sFilterConfig;

    hcan1.Instance = CAN1;
    hcan1.Init.Prescaler = 6;
    hcan1.Init.Mode = CAN_MODE_NORMAL;
    hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
    hcan1.Init.TimeSeg1 = CAN_BS1_12TQ;
    hcan1.Init.TimeSeg2 = CAN_BS2_1TQ;
    hcan1.Init.TimeTriggeredMode = DISABLE;
    hcan1.Init.AutoBusOff = DISABLE;
    hcan1.Init.AutoWakeUp = DISABLE;
    hcan1.Init.AutoRetransmission = DISABLE;
    hcan1.Init.ReceiveFifoLocked = DISABLE;
    hcan1.Init.TransmitFifoPriority = DISABLE;

    if (HAL_CAN_Init(&hcan1) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }

    sFilterConfig.FilterBank = 0;
    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
    sFilterConfig.FilterIdHigh = 0x0000;
    sFilterConfig.FilterIdLow = 0x0000;
    sFilterConfig.FilterMaskIdHigh = 0x0000;
    sFilterConfig.FilterMaskIdLow = 0x0000;
    sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
    sFilterConfig.FilterActivation = ENABLE;
    sFilterConfig.SlaveStartFilterBank = 14;
    HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig);

    if (HAL_CAN_Start(&hcan1) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }

    /* Enable interrupts. */
    HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
}

void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    if (hcan->Instance == CAN1)
    {
        /* Peripheral clock enable */
        __HAL_RCC_CAN1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();

        /**CAN1 GPIO Configuration
        PA11     ------> CAN1_RX
        PA12     ------> CAN1_TX
        */
        GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        /* CAN1 RX0 interrupt Init */
        HAL_NVIC_SetPriority(CAN1_RX0_IRQn, BSP_CAN_IRQ_PRE_PRI, BSP_CAN_IRQ_SUB_PRI);
        HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
    }
}

void HAL_CAN_MspDeInit(CAN_HandleTypeDef *hcan)
{
    if (hcan->Instance == CAN1)
    {
        /* Peripheral clock disable */
        __HAL_RCC_CAN1_CLK_DISABLE();

        /**CAN1 GPIO Configuration
        PA11     ------> CAN1_RX
        PA12     ------> CAN1_TX
        */
        HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11 | GPIO_PIN_12);

        /* CAN1 interrupt DeInit */
        HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn);
    }
}

/**
* @brief This function handles CAN1 RX0 interrupts.
*/
void CAN1_RX0_IRQHandler(void)
{
    BSP_CAN_ENTER_ISR();
    HAL_CAN_IRQHandler(&hcan1);
    BSP_CAN_EXIT_ISR();
}

/*
    [..]
    This subsection provides the following callback functions:
      (+) HAL_CAN_TxMailbox0CompleteCallback
      (+) HAL_CAN_TxMailbox1CompleteCallback
      (+) HAL_CAN_TxMailbox2CompleteCallback
      (+) HAL_CAN_TxMailbox0AbortCallback
      (+) HAL_CAN_TxMailbox1AbortCallback
      (+) HAL_CAN_TxMailbox2AbortCallback
      (+) HAL_CAN_RxFifo0MsgPendingCallback
      (+) HAL_CAN_RxFifo0FullCallback
      (+) HAL_CAN_RxFifo1MsgPendingCallback
      (+) HAL_CAN_RxFifo1FullCallback
      (+) HAL_CAN_SleepCallback
      (+) HAL_CAN_WakeUpFromRxMsgCallback
      (+) HAL_CAN_ErrorCallback
*/

/**
  * @brief  can msg handle
  */
#define CAN_QUEUE_LENGTH            5
#define CAN_MSG_ITEM_SIZE       sizeof( can_msg_t )

static uint32_t can_rx_overflow;
static QueueHandle_t can_msg_queue_handle;
static StaticQueue_t can_msg_queue;
static can_msg_t can_msg_queue_storage_area[ CAN_QUEUE_LENGTH ];

__weak void hd_sensor_task_can_rx_isr_cb(BaseType_t *pxHigherPriorityTaskWoken) {}

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    can_msg_t rx_msg;

    if (hcan == &hcan1 &&
            HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_msg.header, rx_msg.data) == HAL_OK)
    {
        if (can_msg_queue_handle)
        {
            BaseType_t xHigherPriorityTaskWoken = pdFALSE;
            if (xQueueIsQueueFullFromISR(can_msg_queue_handle) != pdFALSE)
            {
                can_msg_t temp_msg;
                xQueueReceiveFromISR(can_msg_queue_handle, &temp_msg, &xHigherPriorityTaskWoken);
                can_rx_overflow = pdTRUE;
            }
            xQueueSendToBackFromISR(can_msg_queue_handle, &rx_msg, &xHigherPriorityTaskWoken);
            hd_sensor_task_can_rx_isr_cb(&xHigherPriorityTaskWoken); // notify hd_sensor_task to handle can message
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        }
    }
}

int32_t bsp_can_send_msg(can_tx_header_t *header, uint8_t data[], uint32_t *tx_mail_bpx)
{
    if (HAL_CAN_AddTxMessage(&hcan1, header, data, tx_mail_bpx) != HAL_OK)
    {
        return BSP_CAN_FAIL;
    }
    return BSP_CAN_OK;
}

int32_t bsp_can_recv_msg(can_msg_t *msg, uint32_t timeout)
{
    int32_t res = BSP_CAN_OK;

    if (can_rx_overflow != pdFALSE)
    {
        can_rx_overflow = pdFALSE;
        res = BSP_CAN_RX_OVERFLOW;
    }

    if (can_msg_queue_handle &&
            xQueueReceive(can_msg_queue_handle, msg, pdMS_TO_TICKS(timeout)) == pdPASS)
    {
        return res;
    }

    return BSP_CAN_FAIL;
}

int32_t bsp_can_init(void)
{
    MX_CAN1_Init();

    can_rx_overflow = pdFALSE;

    can_msg_queue_handle = xQueueCreateStatic(CAN_QUEUE_LENGTH, CAN_MSG_ITEM_SIZE,
                           (uint8_t *)can_msg_queue_storage_area, &can_msg_queue);

    if (can_msg_queue_handle == NULL)
    {
        DBG_LOGE(TAG, "CAN message queue create ERROR");
    }

    return 0;
}

/**
  * @}
  */

/******************* (C)COPYRIGHT 2018 ***** END OF FILE *********************/
